/*
默认情况下 effectFn会立即执行，但是我们需要懒执行(lazy)的effect，即不是立刻执行的effectFn，而是希望它执行的时候，才执行，比如计算属性
effect(()=>{
  console.log(obj.foo)
}, {
  lazy: true
})
*/

const bucket = new WeakMap()

let activeEffect
const effectStack = []
function effect(fn, options = {}) {
  const effectFn = () => {
    cleanup(effectFn)
    activeEffect = effectFn
    effectStack.push(effectFn)
    const res = fn()
    effectStack.pop()
    activeEffect = effectStack[effectStack.length - 1]
    return res
  }
  effectFn.options = options
  effectFn.deps = []
  // 新增， 如果不是lazy，就立刻执行
  if (!options.lazy) {
    effectFn()
  }
  return effectFn
}

function cleanup(effectFn) {
  for (let i = 0; i < effectFn.deps.length; i++) {
    const deps = effectFn.deps[i]
    deps.delete(effectFn)
  }
  effectFn.deps.length = 0
}

function track(target, key) {
  if (!activeEffect) return
  let depsMap = bucket.get(target)
  if (!depsMap) bucket.set(target, (depsMap = new Map()))
  let deps = depsMap.get(key)
  if (!deps) depsMap.set(key, (deps = new Set()))
  deps.add(activeEffect)
  activeEffect.deps.push(deps)
}

function trigger(target, key) {
  const depsMap = bucket.get(target)
  if (!depsMap) return
  const deps = depsMap.get(key)
  if (!deps) return
  const effectsToRun = new Set()
  deps.forEach((effectFn) => {
    if (effectFn !== activeEffect) {
      effectsToRun.add(effectFn)
    }
  })
  effectsToRun.forEach((effectFn) => {
    // 调度器使用
    if (effectFn.options.scheduler) {
      effectFn.options.scheduler(effectFn)
    } else {
      effectFn()
    }
  })
}

// 实现计算属性
function computed(getter) {
  let val
  let dirty = true // 脏值标记，如果dirty则执行effectFn， 否则返回缓存val
  const effectFn = effect(getter, {
    lazy: true,
    scheduler() {
      dirty = true // 调度器执行时，将dirty改为true
      trigger(res, 'value') // 在effect中读取计算属性的value，需要手动trigger effectFn的执行
    },
  })

  const res = {
    get value() {
      if (dirty) {
        val = effectFn()
        dirty = false
      }
      // 计算属性，需要手动track
      track(res, 'value')
      return val
    },
  }
  return res
}

// 数据源
const data = { foo: 'hello', bar: 1 }

const obj = new Proxy(data, {
  get(target, key) {
    track(target, key)
    return Reflect.get(target, key)
  },
  set(target, key, val) {
    Reflect.set(target, key, val)
    trigger(target, key)
    return true
  },
})

const concatStr = computed(() => {
  console.count('computed count')
  return obj.foo + obj.bar
})
/* console.log('computed:', concatStr.value)
obj.bar++
console.log('computed:', concatStr.value) // 多次调用，计算属性应该有缓存值
console.log('computed:', concatStr.value) */

// effect中读取计算属性时，需要手动的track和trigger计算属性对象的value
effect(() => {
  console.log('effect读取computed:', concatStr.value)
})

obj.bar++
